/*
 *  Autores: Claudio Djohnnatha, David Guilherme, Jailson Lima e Jordan Lira.
 *
 *  Projeto da disciplina Linguagem de Programação 2.
 *
 *  Arquivo: ProtocolServer.java
 *
 *  Descrição: O ProtocolServer serve para gerenciar a comunição do seu ClientApplication associado
 *  com o Servidor ( Server ).
 *
 *  Data: 27/08/2013
 *
 */



import java.net.Socket;
import java.net.ServerSocket;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

public class ProtocolServer implements Runnable
{
   private Socket s;
   private String fromClient;
   private ProtocolComunication protocol;
   private ManagementProtocol management;

   // Essas conecções ( "portas" ) in e out se cominucam com as coneções ( "portas" ) out e in
   // respectivamente de cada ClientApplication que está associado com ele.
   private DataInputStream in;
   private DataOutputStream out;


   public ProtocolServer( Socket ns, ManagementProtocol m )
   {
      s = ns;
      management = m;
   }

   public void run()
   {
      try
      {
         in = new DataInputStream( s.getInputStream() );
         out = new DataOutputStream( s.getOutputStream() );

         while( true )
         {
            fromClient = in.readUTF(); // Ler o que o seu ClientApplication escreveu.

// System.out.println( fromClient );

            if ( fromClient.equals( "PING" ) )
            {
               try { out.writeUTF( "PING" ); } catch ( IOException e ) {}
            }
            else
            if ( fromClient.equals( "CONNECTING" ) ) // Pedido de conecção do ClientApplication.
            {
               String list = management.listDocuments() + " ACCEPT_CONNECTING";

               try { out.writeUTF( list ); } catch ( IOException e ) {} // Reposta de conecção aceita para o seu ClientApplication, e mais a lista de documentos.
            }
            else
               if ( fromClient.equals( "DESCONNECTING" ) ) // Aviso de desconecção do ClientApplication para o ProtocolServer.
               {
                  out.writeUTF( "DESCONNECTED" ); // Reposta do ProtocolServer para o ClientApplication indicando que ele está desconectado.
                  break;
               }
               else
               {

                  String[] tokens = fromClient.split( " " );
                  String title;

                  // Operações de escrita: W
                  // tokens[ 0 ] => DELETE/BACKSPACE/SPACE/ENTER/TAB/CHARACTER
                  // tokens[ 1 ] => position
                  // tokens[ 2 ] => length
                  // tokens[ 3 ] => W [ se a operação não for CHARECTER ]
                  // tokens[ 3 ] => char [ se a operação for CHARECTER ]
                  // tokens[ 4 ] => W [ se a operação for CHARECTER ]

                  // Operações sobre os arquivos do servidor: F
                  // tokens[ length - 3 ] => Titulo [ Depedendo da operação ]
                  // tokens[ length - 2 ] => LIST_DOC/NEW_DOC/OPEN_DOC/SAVE_DOC/CLOSE_DOC/RENAME_DOC/DELETE_DOC
                  // tokens[ length - 1 ] => F


                  // Verifica se o cliente esta enviando uma operação sobre arquivo do servidor, ou uma
                  // operação de escrita em um arquivo já aberto.
                  if ( tokens[ tokens.length - 1 ].equals("F") )
                  {
                     if ( tokens[ tokens.length - 2 ].equals( "LIST_DOC" ) )
                     {
                        String list = management.listDocuments() + " LIST_DOC";

                        try { out.writeUTF( list ); } catch ( IOException e ) {}
                     }
                     else
                        if ( tokens[ tokens.length - 2 ].equals( "NEW_DOC" ) )
                        {
                           // Retira a referência ao documento atualmente aberto se for o caso.
                           if ( protocol != null )
                              protocol.removeListServer( this );

                           title = fromClient.substring( 0, fromClient.lastIndexOf( " NEW_DOC" ) );
                           protocol = management.newDocument( title );
                           protocol.addListServer( this );

                           try { out.writeUTF( "NEW_DOC" ); } catch ( IOException e ) {}
                        }
                        else
                           if ( tokens[ tokens.length - 2 ].equals( "OPEN_DOC" ) )
                           {
                              ProtocolComunication p = protocol; // guarda referência do Protocolo antigo.
                              try
                              {
                                 title = fromClient.substring( 0, fromClient.lastIndexOf( " OPEN_DOC" ) );
                                 protocol = management.openDocument( title );
                                 protocol.addListServer( this );

                                 // Retira a referência ao documento aberto anteriormente se for o caso.
                                 if ( p != null )
                                    p.removeListServer( this );

                                 out.writeUTF( protocol.getText() + " OK OPEN_DOC" );
                              }
                              catch ( Exception e )
                              {
                                 out.writeUTF( "FAIL OPEN_DOC" );
                              }
                           }
                           else
                              if ( tokens[ tokens.length - 2 ].equals( "SAVE_DOC" ) )
                              {
                                 management.saveDocument( protocol.getDocument() );
                                 try { out.writeUTF( "SAVE_DOC" ); } catch ( IOException e ) {}
                              }
                              else
                                 if ( tokens[ tokens.length - 2 ].equals( "CLOSE_DOC" ) )
                                 {
                                    // Retira a referência ao documento atualmente aberto se for o caso.
                                    if ( protocol != null )
                                       protocol.removeListServer( this );

                                    protocol = null;

                                    try { out.writeUTF( "CLOSE_DOC" ); } catch ( IOException e ) {}
                                 }
                                 else
                                    if ( tokens[ tokens.length - 2 ].equals( "RENAME_DOC" ) )
                                    {
                                       title = fromClient.substring( 0, fromClient.lastIndexOf( " RENAME_DOC" ) );
                                       management.renameDocument( protocol, title );
                                    }
                                    else
                                       if ( tokens[ tokens.length - 2 ].equals( "DELETE_DOC" ) )
                                       {
                                          try
                                          {
                                             management.deleteDocument( protocol );

                                             String list = management.listDocuments() + " OK DELETE_DOC";
                                             out.writeUTF( list );
                                          }
                                          catch ( Exception e )
                                          {
                                             out.writeUTF( "FAIL DELETE_DOC" );
                                          }
                                       }
                  }
                  else // tokens[ length - 1 ] só pode ser W
                  {
                     protocol.processLine( fromClient, this );
                  }

               } // fim do else maior

         } // fim do while.

         // Retirar o ProtocolServer finalizado da LinkedList do ProtocolComunication.
         if ( protocol != null )
            protocol.removeListServer( this );

         // Fecha os stream.
         in.close();
         out.close();
         s.close();
      }
      catch ( IOException e )
      {
         // System.out.println( "CLIENTE CAIU!" );

         try
         {
            // Retirar o ProtocolServer finalizado da LinkedList do ProtocolComunication.
            if ( protocol != null )
               protocol.removeListServer( this );

            // Fecha os stream.
            in.close();
            out.close();
            s.close();
         }
         catch ( IOException exception ) {}
      }

   } // fim do método run.

   // Método para atualizar.
   public void update( String synchronized_text )
   {
      try { out.writeUTF( synchronized_text ); } catch ( IOException e ) {}
   }

   public void renameTitle( String new_title )
   {
      try { out.writeUTF( new_title + " RENAME_DOC" ); } catch ( IOException e ) {}
   }

   // Atualização enviada do servidor se algum documento for renomeado ou excluído.
   public void updateServer( String message_update )
   {
      try { out.writeUTF( message_update ); } catch ( IOException e ) {}
   }

}